<template>
    <div id="containerId"></div>
</template>

<script setup>
import { onMounted } from "vue";
// 原理：基于 canvas 标签绘制像素点（位置，颜色等）在平面上计算出 3D 效果
// 下载并引入three库
import * as THREE from "three";
// 引入轨道控制器
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 引入性能监视器
import Stats from "three/examples/jsm/libs/stats.module.js";
import { Theme } from "ant-design-vue";

// 全局变量
let scene;  // 存储场景
let camera; // 存储摄像机
let renderer;   // 存储渲染器
let controls;   // 存储轨道控制器
let cubeArr=[]; // 存储多个立方体
let statsArr=[]; // 存储性能监视器 

onMounted(() => {
    // 1.场景：放置物体的容器（世界）
    scene = new THREE.Scene();
    // 2.摄像机：类似于人眼，决定看到多大的空间范围
    camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
    // 移动摄像机
    camera.position.set(12, 12, 12)
    // 3.渲染器：接收`场景`和`摄像机`，计算在浏览器上渲染的最终`2D画面`。
    renderer = new THREE.WebGLRenderer({antialias: true});
    // 3.1 将输出canvas的大小调整为(width, height)
    renderer.setSize( window.innerWidth, window.innerHeight );
    // 添加到页面中
    document.getElementById("containerId").appendChild( renderer.domElement );
    // 创建坐标轴，并添加场景世界中
    scene.add(createAxes(10))
    // 创建性能监视器
    for(let i=0; i<3; i++) {
        let sta = createStats(i, `${90*2 - 90*i}px`, 0);
        document.getElementById("containerId").appendChild(sta.domElement)
        statsArr.push(sta)
    }
    // 创建轨道控制器
    controls = createOrbit(camera, renderer.domElement)

    // 创建地面
    const plane = createFloor();
    scene.add(plane);
    // 创建球体
    const sphere = createStandardSphere();
    scene.add(sphere);
    // 创建光源、光源辅助线
    const light = createAmbientLight();
    scene.add(light)


    // 把 场景+摄像机 传递给渲染器进行渲染
    renderer.render(scene, camera)
    // 调用动画
    animate();
    // 响应屏幕大小变化
    resize();
})


/**
 * 页面变化后重新绘制
*/
function resize() {
    window.addEventListener("resize", () => {
        // 设置画布（世界黑色背景）的宽高
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 调整摄像机宽高比（防止被压缩）
        camera.aspect = window.innerWidth / window.innerHeight
        // 更新摄像机矩阵空间（让摄像机重新计算每个像素点位置和大小）
        camera.updateProjectionMatrix();
    })
}


/**
 * 添加动画效果
*/
function animate() {
    // 根据浏览器刷新帧率（默认1s 60次）不断递归调用此渲染函数，为了让render不断渲染最新的画面
    requestAnimationFrame( animate );
    // 轨道控制器属性改变，需要更新轨道控制器才有效果
    controls.update();
    // 不断更新当下最新 FPF 等状态数值
    statsArr.forEach(item => {
        item.update();
    })
    renderer.render( scene, camera );
}


/**
 * 创建坐标轴
*/
function createAxes(size=1) {
    const axesHelper = new THREE.AxesHelper( size );
    return axesHelper
}


/**
 * 创建轨道控制器
*/
function createOrbit (camera, domElement) {
    const controls = new OrbitControls( camera, domElement )
    controls.update();
    return controls
}

/**
 * 创建性能监视器
*/
function createStats(type, right, top) {
    let stats = new Stats();
    stats.setMode(type); // 0:每秒刷新帧数，1：每帧用时，2：内存占用
    stats.domElement.style.position = "fixed";
    stats.domElement.style.width = "fit-content";
    // 取消掉原先的left样式，否则后面设置的right样式不生效
    stats.domElement.style.left = "";
    stats.domElement.style.right = right;
    stats.domElement.style.top = top;
    return stats
}


/**
 * 创建标准网格材质的球体
*/
function createStandardSphere() {
    const geometry = new THREE.SphereGeometry( 2, 32, 16 );
    const material = new THREE.MeshStandardMaterial( { color: 0xffff00 } );
    const sphere = new THREE.Mesh( geometry, material );
    return sphere
}

/**
 * 创建平行光
*/
function createDirectionalLight() {
    // 创建光源
    const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    // 调整光源位置
    directionalLight.position.set(5, 5, 5);
    // 添加光源辅助对象，参数1：光源对象(给哪个光源创建辅助线)，参数2：辅助线的宽高
    const directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight, 1);
    
    return { directionalLight, directionalLightHelper }
}


/**
 * 创建地面
*/
function createFloor() {
    // 1.创建几何体
    const geometry = new THREE.PlaneGeometry(10, 10);
    // 2.创建材料
    const material = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide });
    // 3.生成物体
    const plane = new THREE.Mesh(geometry, material);
    // 旋转
    plane.rotation.x = -Math.PI / 2;
    // 调整位置
    plane.position.y = -2
    return plane
}


/**
 * 创建环境光
*/
function createAmbientLight() {
    // 环境光（基础光源）
    // 特点1：照亮所有物体所有面
    // 特点2：没有方向，没有阴暗面，没有光斑
    // 注意：从three.js@0.155.0版本开始往后，光源照射的效果做了重构。
    const light = new THREE.AmbientLight(0xffffff, 1);

    return light
}
</script>